package com.songyz.image.compress.controller;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import javax.servlet.http.HttpServletRequest;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.InputStreamResource;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;

import com.songyz.image.compress.dto.ImageCompressBatchParam;
import com.songyz.image.compress.dto.ImageCompressDTO;
import com.songyz.image.compress.dto.ImageCompressSingleParam;
import com.songyz.image.compress.dto.NotifyDTO;
import com.songyz.image.compress.enums.Compress;
import com.songyz.image.compress.service.WebSocketService;
import com.songyz.image.compress.utils.CompressUtils;
import com.songyz.image.compress.utils.ExecPool;

@RestController
public class ImageController {

    private static Logger logger = LoggerFactory.getLogger(ImageController.class);

    private Map<String, Compress> cacheFileMap = new HashMap<>();
    private Map<String, ImageCompressDTO> cacheCompressMap = new HashMap<>();

    @Autowired
    public WebSocketService socketService;

    @PostMapping("/upload")
    public List<String> upload(String sessionId, HttpServletRequest request) {
        Map<String, MultipartFile> fileMap = ((MultipartHttpServletRequest) request).getFileMap();
        if (Objects.isNull(fileMap) || fileMap.isEmpty())
            return Collections.emptyList();

        logger.debug("sessionId：{}", sessionId);

        return fileMap.values().stream().map(file -> {
            try {
                File target = new File(CompressUtils.getSourcePath(sessionId, file.getOriginalFilename()));

                target.getParentFile().mkdirs();

                if (!target.exists())
                    file.transferTo(target);

                cacheFileMap.put(target.getAbsolutePath(), Compress.WAIT);

                return target.getAbsolutePath();
            }
            catch (IllegalStateException | IOException e) {
                logger.error(e.getMessage(), e);
                return null;
            }
        }).collect(Collectors.toList());
    }

    @PostMapping("/compress/single")
    public boolean compress(@RequestBody ImageCompressSingleParam param) throws IOException {
        String fileName = param.getFileName();
        String key = CompressUtils.getSourcePath(param.getSessionId(), fileName);

        Compress status = cacheFileMap.get(key);
        if (Objects.isNull(status) || Compress.DOING == status)
            return false;

        if (Compress.SUCCESS == status)
            return true;

        cacheFileMap.put(key, Compress.DOING);

        CompressUtils.startCompress(param, param.getSessionId(), fileName, file -> {
            cacheCompressMap.put(key, file);

            cacheFileMap.put(key, Compress.SUCCESS);
            logger.debug("file:{},执行压缩成功", key);

            socketService.sendMsg(param.getSessionId(), "/compress/detail", file);
        });

        return true;
    }

    @PostMapping("/compress/batch")
    public boolean compress(@RequestBody ImageCompressBatchParam param) throws IOException {

        List<Future<ImageCompressDTO>> futures = param.getFileNames().stream().map(fileName -> {

            String key = CompressUtils.getSourcePath(param.getSessionId(), fileName);

            Compress status = cacheFileMap.get(key);
            if (Objects.isNull(status) || Compress.DOING == status || Compress.SUCCESS == status)
                return null;

            cacheFileMap.put(key, Compress.DOING);

            ImageCompressDTO result = new ImageCompressDTO();
            result.setFileName(fileName);
            result.setStatus(Compress.DOING);
            socketService.sendMsg(param.getSessionId(), "/compress/detail", result);

            return CompressUtils.startCompress(param, param.getSessionId(), fileName, file -> {
                cacheCompressMap.put(key, file);

                cacheFileMap.put(key, Compress.SUCCESS);
                logger.debug("file:{},执行压缩成功", key);
                socketService.sendMsg(param.getSessionId(), "/compress/detail", file);
            });
        }).filter(Objects::nonNull).collect(Collectors.toList());

        ExecPool.submit(() -> {
            while (true) {
                int count = (int) futures.stream().filter(f -> f.isDone()).count();
                logger.info("当前压缩完成个数 count:{}", count);
                if (Objects.equals(count, futures.size())) {
                    NotifyDTO notify = NotifyDTO.success(futures.size() + "个文件压缩完成");
                    notify.setFinished(true);
                    socketService.sendMsg(param.getSessionId(), "/compress/notify", notify);
                    break;
                }

                try {
                    TimeUnit.SECONDS.sleep(1);
                }
                catch (InterruptedException e) {
                    logger.error(e.getMessage(), e);
                }
            }
        });

        socketService.sendMsg(param.getSessionId(), "/compress/notify", NotifyDTO.info("开始压缩，文件个数:" + futures.size() + " 个"));

        return true;
    }

    @GetMapping("/query")
    public ImageCompressDTO query(String sessionId, String fileName) {
        ImageCompressDTO result = new ImageCompressDTO();
        result.setFileName(fileName);
        String key = CompressUtils.getSourcePath(sessionId, fileName);

        Compress status = cacheFileMap.getOrDefault(key, Compress.NO_EXISTS);

        result.setStatus(status);

        if (Compress.SUCCESS == status) {
            return cacheCompressMap.get(key);
        }
        else {
            return result;
        }
    }

    @GetMapping("/preview")
    public ResponseEntity<InputStreamResource> preview(String sessionId, String fileName) throws FileNotFoundException {
        String key = CompressUtils.getTargetPath(sessionId, fileName);
        return ResponseEntity.ok()//
                .contentType(MediaType.IMAGE_JPEG)//
                .header("Content-disposition", "attachment; filename=" + fileName)//
                .body(new InputStreamResource(new FileInputStream(key)));
    }

    @GetMapping("/download")
    public ResponseEntity<InputStreamResource> download(String sessionId, String fileName) throws FileNotFoundException {
        String key = CompressUtils.getTargetPath(sessionId, fileName);
        return ResponseEntity.ok()//
                .contentType(MediaType.APPLICATION_OCTET_STREAM)//
                .header("Content-disposition", "attachment; filename=" + fileName)//
                .body(new InputStreamResource(new FileInputStream(key)));
    }

}
